package com.unione.unione_network.tcp_socket

import java.io.IOException
import java.io.InputStream
import java.nio.ByteOrder

/**
 * 可变长度的粘包处理，使用于协议中有长度字段
 * 例：协议为:       type+dataLen+data+md5
 *                  type:命名类型，两个字节
 *                  dataLen:data字段的长度,两个字节
 *                  data:数据字段,长度不定，长度为dataLen
 *                  md5:md5字段，8个字节
 *      使用：      1.byteOrder:首先确定大小端，ByteOrder.BIG_ENDIAN or ByteOrder.LITTLE_ENDIAN;
 *                 2.lenSize:len字段的长度，这个例子为2
 *                 3.lenIndex：len字段的位置，这个例子为2，因为len字段前面为type，它长度为2
 *                 4.offset：整个包的长度-len，这个例子是，type+dataLen+md5 三个字段的长度，也就是2+2+8=12
 *
 */
class VariableLenStickPackageHelper(
    private val byteOrder: ByteOrder,
    private val lenSize: Int,
    private val lenIndex: Int,
    private val offset: Int
) : AbsStickPackageHelper {

    private val mBytes = mutableListOf<Byte>()
    private var lenStartIndex = lenIndex
    private var lenEndIndex = lenIndex + lenSize - 1

    init {
        require(lenStartIndex <= lenEndIndex) { "lenStartIndex must be less than or equal to lenEndIndex" }
    }

    private fun getLen(src: ByteArray, order: ByteOrder): Int {
        var re = 0
        when (order) {
            ByteOrder.BIG_ENDIAN -> src.forEach { re = (re shl 8) or (it.toInt() and 0xFF) }
            ByteOrder.LITTLE_ENDIAN -> for (i in src.indices.reversed()) {
                re = (re shl 8) or (src[i].toInt() and 0xFF)
            }
        }
        return re
    }

    override fun execute(inputStream: InputStream): ByteArray? {
        mBytes.clear()
        var count = 0
        var len: Int
        var temp: Byte
        var msgLen = -1
        val lenField = ByteArray(lenSize)

        try {
            while (true) {
                len = inputStream.read()
                if (len == -1) break
                temp = len.toByte()
                if (count in lenStartIndex..lenEndIndex) {
                    lenField[count - lenStartIndex] = temp
                    if (count == lenEndIndex) {
                        msgLen = getLen(lenField, byteOrder)
                    }
                }
                count++
                mBytes.add(temp)
                if (msgLen != -1) {
                    if (count == msgLen + offset) break
                    else if (count > msgLen + offset) {
                        len = -1 // Mark as error
                        break
                    }
                }
            }
        } catch (e: IOException) {
            e.printStackTrace()
            return null
        }

        if (len == -1) return null

        val result = ByteArray(mBytes.size)
        for (i in result.indices) {
            result[i] = mBytes[i]
        }
        return result
    }
}